//
//  FirstViewController.swift
//  MyLocations
//
//  Created by cygarm on 15/12/4.
//  Copyright © 2015年 cygarm. All rights reserved.
//

import UIKit
import CoreLocation
import CoreData
import QuartzCore
import AudioToolbox

class CurrentLocationViewController: UIViewController, CLLocationManagerDelegate {

	@IBOutlet weak var messageLabel: UILabel!
	@IBOutlet weak var latitudeLabel: UILabel!
	@IBOutlet weak var longitudeLabel: UILabel!
	@IBOutlet weak var addressLabel: UILabel!
	@IBOutlet weak var tagButton: UIButton!
	@IBOutlet weak var getButton: UIButton!
	@IBOutlet weak var latitudeTextLabel: UILabel!
	@IBOutlet weak var longitudeTextLabel: UILabel!
	@IBOutlet weak var containerView: UIView!
	
	var logoVisible = false
	lazy var logoButton: UIButton = {
		let button = UIButton(type: .Custom) as UIButton
		button.setBackgroundImage(UIImage(named: "Logo"), forState: .Normal)
		button.sizeToFit()
		button.addTarget(self, action: Selector("getLocation"), forControlEvents: .TouchUpInside)
		button.center.x = CGRectGetMidX(self.view.bounds)
		button.center.y = 220
		return button
	}()
	var soundID: SystemSoundID = 0
	
	let locationManager = CLLocationManager()
	var location: CLLocation?
	var updatingLocation = false
	var lastLocationError: NSError?
	
	let geocoder = CLGeocoder()
	var placemark: CLPlacemark?
	var performingReverseGeocoding = false
	var lastGeocodingError: NSError?
	
	var timer: NSTimer?
	
	var managedObjectContext: NSManagedObjectContext!
	
	override func viewDidLoad() {
		super.viewDidLoad()
		updateLabels()
		configureGetButton()
		loadSoundEffect("Sound.caf")
	}

	//MARK: Logo view
	func showLogoView() {
		if !logoVisible {
			logoVisible = true
			containerView.hidden = true
			view.addSubview(logoButton)
		}
	}
	
	func hideLogoView() {
		if !logoVisible {
			return
		}
		
		logoVisible = false
		containerView.hidden = false
		
		containerView.center.x = view.bounds.size.width * 2
		containerView.center.y = 40 + containerView.bounds.size.height / 2
		
		let centerX = CGRectGetMaxX(view.bounds)
		let panelMover = CABasicAnimation(keyPath: "position")
		panelMover.removedOnCompletion = false
		panelMover.fillMode = kCAFillModeForwards
		panelMover.duration = 0.6
		panelMover.fromValue = NSValue(CGPoint: containerView.center)
		panelMover.toValue = NSValue(CGPoint: CGPoint(x: centerX, y: containerView.center.y))
		panelMover.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
		panelMover.delegate = self //this animation takes longest,set delegate to get notified when the entire animation is over.
		containerView.layer.addAnimation(panelMover, forKey: "panelMover")
		
		let logoMover = CABasicAnimation(keyPath: "position")
		logoMover.removedOnCompletion = false
		logoMover.fillMode = kCAFillModeForwards
		logoMover.duration = 0.5
		logoMover.fromValue = NSValue(CGPoint: logoButton.center)
		logoMover.toValue = NSValue(CGPoint: CGPoint(x: -centerX, y: logoButton.center.y))
		logoMover.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
		logoButton.layer.addAnimation(logoMover, forKey: "logoMover")
		
		let logoRotator = CABasicAnimation(keyPath: "transform.rotation.z")
		logoRotator.removedOnCompletion = false
		logoRotator.fillMode = kCAFillModeForwards
		logoRotator.duration = 0.5
		logoRotator.fromValue = 0.0
		logoRotator.toValue = -2 * M_PI
		logoRotator.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
		logoButton.layer.addAnimation(logoRotator, forKey: "logoRotator")
	}
	
	override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
		containerView.layer.removeAllAnimations()
		containerView.center.x = view.bounds.size.width / 2
		containerView.center.y = 40 + containerView.bounds.size.height / 2
		
		logoButton.layer.removeAllAnimations()
		logoButton.removeFromSuperview()
	}
	
	//MARK: Sound Effect
	func loadSoundEffect(name: String) {
		if let path = NSBundle.mainBundle().pathForResource(name, ofType: nil) {
			let fileURL = NSURL(fileURLWithPath: path, isDirectory: false)
//			if fileURL == nil {
//				print("NSURL is nil for path: \(path)")
//				return
//			}
			
			let error = AudioServicesCreateSystemSoundID(fileURL, &soundID)
			if error != kAudioServicesNoError {
				print("Error code \(error) loading sound at path: \(path)")
				return
			}
		}
	}
	
	func unloadSoundEffect() {
		AudioServicesDisposeSystemSoundID(soundID)
		soundID = 0
	}
	
	func playSoundEffect() {
		AudioServicesPlaySystemSound(soundID)
	}
	
	//MARK: IBAction
	@IBAction func getLocation() {
		let authStatus: CLAuthorizationStatus = CLLocationManager.authorizationStatus()
		
		if authStatus == .NotDetermined {
			// has not asked for permisson yet
			locationManager.requestWhenInUseAuthorization()
			return
		}
		if authStatus == .Denied || authStatus == .Restricted	{
			showLocationServicesDeniedAlert()
			return
		}
		
		if logoVisible {
			hideLogoView()
		}
		
		if updatingLocation {
			stopLocationManager()
		} else {
			location = nil
			lastLocationError = nil
			placemark = nil
			lastLocationError = nil
			startLocationManager()
		}
		updateLabels()
	}
	
	func configureGetButton() {
		let spinnerTag = 1000
		
		if updatingLocation {
			getButton.setTitle("Stop", forState: .Normal)
			
			if view.viewWithTag(spinnerTag) == nil {
				let spinner = UIActivityIndicatorView(activityIndicatorStyle: .White)
				spinner.center = messageLabel.center
				spinner.center.y += spinner.bounds.size.height / 2 + 15
				spinner.startAnimating()
				spinner.tag = spinnerTag
				containerView.addSubview(spinner)
			}
		} else {
			getButton.setTitle("Get My Locaion", forState: .Normal)
			
			if let spinner = view.viewWithTag(spinnerTag) {
				spinner.removeFromSuperview()
			}
		}
	}
	
	//MARK: CLLocationManagerDelegate
	func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
		print("didFailWithError \(error)")
		
		if error.code == CLError.LocationUnknown.rawValue {
			return
		}
		
		lastLocationError = error
		stopLocationManager()
		updateLabels()
	}
	
	func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
		let newLocation = locations.last
		print("didUpdateLocations \(newLocation)")
		
		/*
		too old
		*/
		if newLocation?.timestamp.timeIntervalSinceNow < -5 {
			return
		}
		/*
		'horizontalAccuracy < 0' are invalid measuerments
		*/
		if newLocation?.horizontalAccuracy < 0 {
			return
		}
		
		var distance = CLLocationDistance(DBL_MAX)
		if let locationTmp = location {
			distance = (newLocation?.distanceFromLocation(locationTmp))!
		}
		
		if location == nil || location!.horizontalAccuracy > newLocation?.horizontalAccuracy {
			lastLocationError = nil
			location = newLocation
			
			if newLocation?.horizontalAccuracy <= locationManager.desiredAccuracy {
				print("*** We're done!")
				stopLocationManager()
				if distance > 0 {
					performingReverseGeocoding = false
				}
			}
			updateLabels()
			
			/*
			由于境外经纬度无法解析
			这里模拟国内经纬度
			*/
			let myLocation = CLLocation(latitude: 39.9167, longitude: 116.3833)
			
			if !performingReverseGeocoding {
				print("*** Going to geocode")
				
				performingReverseGeocoding = true
				geocoder.reverseGeocodeLocation(myLocation, completionHandler: { (placemarks, error) -> Void in
//					print("*** Found placemarks: \(placemarks), error: \(error)")
					
					self.lastLocationError = error
					if error == nil && placemarks?.isEmpty != nil {
						if self.placemark == nil {
							print("FIRST TIME!")
							self.playSoundEffect()
						}
						
						self.placemark = placemarks?.last
					} else {
						self.placemark = nil
					}
					
					self.performingReverseGeocoding = false
					self.updateLabels()
				})
			} else if distance < 1.0 {
				let timeInterval = newLocation?.timestamp.timeIntervalSinceDate(location!.timestamp)
				if timeInterval > 10 {
					print("*** Force done!")
					stopLocationManager()
					updateLabels()
				}
			}
		}
	}
	
	//MARK: helper method
	func showLocationServicesDeniedAlert() {
		let alert = UIAlertController(title: "Location Services Disabled", message: "Please enable location services for this app in Settings.", preferredStyle: .Alert)
		let okAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
		alert.addAction(okAction)
		
		presentViewController(alert, animated: true, completion: nil)
	}
	
	func stringFromPlacemark(placemark: CLPlacemark) -> String {
		return 
			"\(placemark.subThoroughfare ?? "") \(placemark.thoroughfare ?? "")\n" +
			"\(placemark.locality ?? "") \(placemark.administrativeArea ?? "") " +
			"\(placemark.postalCode ?? "")"
	}
	
	func updateLabels() {
		if let location = location {
			latitudeLabel.text = String(format: "%.8f", location.coordinate.latitude)
			longitudeLabel.text = String(format: "%.8f", location.coordinate.longitude)
			tagButton.hidden = false
			messageLabel.text = ""
			
			if let placemark = placemark {
				addressLabel.text = stringFromPlacemark(placemark)
			} else if performingReverseGeocoding {
				addressLabel.text = "Searching for Address..."
			} else if lastGeocodingError != nil {
				addressLabel.text = "Error Finding Address"
			} else {
				addressLabel.text = "No Address Found"
			}
			
			latitudeTextLabel.hidden = false
			longitudeTextLabel.hidden = false
			
		} else {
			latitudeLabel.text = ""
			longitudeLabel.text = ""
			addressLabel.text = ""
			tagButton.hidden = true
			
			var statusMessage: String
			if let error = lastLocationError {
				if error.domain == kCLErrorDomain && error.code == CLError.Denied.rawValue {
					statusMessage = "Location Services Disabled"
				} else {
					statusMessage = "Error Getting Location"
				}
			} else if !CLLocationManager.locationServicesEnabled() {
				statusMessage = "Location Services Disabled"
			} else if updatingLocation {
				statusMessage = "Searching..."
			} else {
				statusMessage = ""
				showLogoView()
			}
			
			messageLabel.text = statusMessage
			
			latitudeTextLabel.hidden = true
			longitudeTextLabel.hidden = true
		}
	}
	
	func startLocationManager() {
		if CLLocationManager.locationServicesEnabled() {
			locationManager.delegate = self
			locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
			locationManager.startUpdatingLocation()
			updatingLocation = true
		}
		configureGetButton()
		
		timer = NSTimer.scheduledTimerWithTimeInterval(60, target: self, selector: Selector("didTimeOut"), userInfo: nil, repeats: false)
	}
	
	func stopLocationManager() {
		if updatingLocation {
			if let timer = timer {
				timer.invalidate()
			}
			
			locationManager.stopUpdatingLocation()
			locationManager.delegate = nil
			updatingLocation = false
		}
		configureGetButton()
	}
	
	func didTimeOut() {
		print("*** Time Out")
		if location == nil {
			stopLocationManager()
			lastLocationError = NSError(domain: "MyLocationsErrorDomain", code: 1, userInfo: nil)
			updateLabels()
			configureGetButton()
		}
	}
	
	//MARK: Segue handle
	override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
		if segue.identifier == "TagLocation" {
			let navigationController = segue.destinationViewController as! UINavigationController
			let controller = navigationController.topViewController as! LocationDetailsViewController
			
			controller.coordinate = location!.coordinate
			controller.placemark = placemark
			controller.managedObjectContext = managedObjectContext
			
		}
	}
}

